package app.exp;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.rygh.core.html.Html;
import org.rygh.core.html.table.HtmlTable;
import org.rygh.core.html.table.TableColumnStyle;
import org.rygh.core.html.table.TableData;
import org.rygh.core.html.table.TableTuple;
import org.rygh.core.model.common.MapList;
import org.rygh.core.util.UIo;
import org.rygh.core.util.UMath;
import org.rygh.core.util.tex.TexTabular;
import org.rygh.semantic.model.rdf.Entity;

import app.App;
import wsi.exp.Exp4WebCallRatio;
import wsi.exp.Exp4WebCallRatio.Exp4Tuple;
import wsi.exp.ExpFolderController;
import wsi.exp.Experiment.Color;
import wsi.function.Function;
import wsi.function.FunctionController;
import wsi.function.RuntimeInfo;
import wsi.script.AppConfig;

public class exp4Merger extends App {
	static class Tuple {
		final Map<Function, Exp4Tuple> map = new HashMap<>();

		String name;
		File dir;

		public Tuple(String name, File dir) {
			this.name = name;
			this.dir = dir;
		}
	}

	public static void main(String[] args) {
		init();

		AppConfig.init();
		AppConfig ac = AppConfig.shared();

		File dir, file0, file1, file2;
		String path = "experiments/exp4";

		final int size = 3, input_size = 100;

		dir = ac.getRunsFolder().getParentFile();
		file0 = new File(dir, "1112_s_yago_100_input_rnd/" + path);
		file1 = new File(dir, "1112_s_yago_100_input_deg_s/" + path);
		file2 = new File(dir, "1112_l_yago_100_input_fb/" + path);

		assert file1.exists() : file1.getAbsolutePath();

		Tuple[] tuples = new Tuple[size];
		tuples[0] = new Tuple("Random", file0);
		tuples[1] = new Tuple("Degree", file1);
		tuples[2] = new Tuple("Feedback", file2);

		exp4Merger e = new exp4Merger(input_size, tuples);
		e.load();
		e.save();

		bye();
	}

	private final Set<Function> functionSet = new HashSet<>();
	private final Tuple[] tuples;
	private final int inputSize;

	public exp4Merger(int inputSize, Tuple[] tuples) {
		this.tuples = tuples;
		this.inputSize = inputSize;
	}

	private void load() {
		Exp4WebCallRatio exp4 = new Exp4WebCallRatio();

		for (Tuple t : tuples) {
			exp4.loadXml4(t.dir, t.map);
			functionSet.addAll(t.map.keySet());
		}
	}

	private void save() {
		saveHtml();
		saveTex();
	}

	private void saveHtml() {
		TableData data = new TableData();
		TableTuple tuple = new TableTuple();
		data.add(tuple);

		int i;
		tuple.add("Web Service", "Function", "Type", "# Valid Inputs");

		for (i = 0; i < 5; i++)
			for (Tuple t : tuples)
				tuple.add(t.name);

		AppConfig ac = AppConfig.shared();

		for (Function function : functionSet) {
			tuple = new TableTuple();
			data.add(tuple);

			RuntimeInfo config = ac.getConfig(function);
			assert config != null : function.getFullName();

			Entity type = config.getType();
			assert type != null : function.getFullName();

			tuple.add(function.getService(), function.getName(),
					type.getValue(), inputSize);

			for (Tuple j : tuples) {
				Exp4Tuple t = j.map.get(function);
				tuple.add(t == null ? "/" : t.webCalls);
			}

			for (Tuple j : tuples) {
				Exp4Tuple t = j.map.get(function);
				tuple.add(t == null ? "/" : inputSize == t.inputSize ? String
						.format("%s%%", t.toRatioString()) : String.format(
						"%s%% (%d/%d)", t.toRatioString(), t.inputSize,
						t.webCalls));
			}

			for (Tuple j : tuples) {
				Exp4Tuple t = j.map.get(function);
				tuple.add(t == null ? "/" : t.min);
			}

			for (Tuple j : tuples) {
				Exp4Tuple t = j.map.get(function);
				tuple.add(t == null ? "/" : String.format("%.2f", t.avg));
			}

			for (Tuple j : tuples) {
				Exp4Tuple t = j.map.get(function);
				tuple.add(t == null ? "/" : t.max);
			}

		}

		data.sort(2);

		//

		Html html = new Html("Experiment 4");
		html.addElement(AppConfig.shared().getConfigHtml());

		HtmlTable table = new HtmlTable("Experiment 4, Web Call Ratio", data);
		table.setHeaderPrefix(String.format("<tr><th colspan=4></th>"
				+ "<th colspan=%d># Web Calls</th>"
				+ "<th colspan=%d>Ratio</th>" + "<th colspan=%d>Min (ms)</th>"
				+ "<th colspan=%d>Avg (ms)</th>"
				+ "<th colspan=%d>Max (ms)</th>", tuples.length, tuples.length,
				tuples.length, tuples.length, tuples.length));

		TableColumnStyle colStyle = table.getColumnStyle();

		int c = tuples.length, a = 4, b = a + c;

		for (i = a; i < b; i++)
			colStyle.set(i, Color.blue);

		a += c * 2;
		b = a + c;
		for (i = a; i < b; i++)
			colStyle.set(i, Color.blue);

		a += c * 2;
		b = a + c;
		for (i = a; i < b; i++)
			colStyle.set(i, Color.blue);

		html.addElement(table);

		String htmlstr = html.toString();

		for (Tuple t : tuples)
			UIo.saveFile(new File(t.dir.getParentFile(),
					ExpFolderController.EXP + "4_result_2.html"), htmlstr);

	}

	private void saveTex() {
		saveTexFull();
		saveTexAggregated();
	}

	private void saveTexAggregated() {
		TableTuple tuple;
		TableData aggr = new TableData();

		tuple = new TableTuple();
		aggr.add(tuple);
		tuple.add("Type", "Random", "Degree", "Feedback");

		MapList<String, Function> mapList = FunctionController.shared()
				.getFunctionByInputTypeMap();

		double ratio, d;
		double[][] dval = new double[3][tuples.length];
		List<List<Double>> dlist = new ArrayList<>();

		for (int j = 0; j < tuples.length; j++)
			dlist.add(new ArrayList<Double>());

		/*
		 * 0: min, 1: avg, 2: max
		 */

		for (Entry<String, List<Function>> e : mapList.entrySet()) {
			String id = e.getKey();

			if (id.equals("identifier"))
				continue;

			tuple = new TableTuple();
			aggr.add(tuple);

			tuple.add(id);

			for (List<Double> list : dlist)
				list.clear();

			Arrays.fill(dval[0], Double.MAX_VALUE);
			Arrays.fill(dval[1], Double.NaN);
			Arrays.fill(dval[2], -Double.MAX_VALUE);

			for (Function f : e.getValue()) {

				for (int j = 0; j < tuples.length; j++) {
					Exp4Tuple t = tuples[j].map.get(f);

					if (t == null) {
						System.out.printf("[info] skip %s\n", f.getFullName());
						continue;
					}

					ratio = t.getRatio();

					if (dval[0][j] > ratio)
						dval[0][j] = ratio;

					if (dval[2][j] < ratio)
						dval[2][j] = ratio;

					dlist.get(j).add(ratio);
				}

				for (int j = 0; j < tuples.length; j++)
					dval[1][j] = UMath.getAverage(dlist.get(j), .0);
			}

			for (int j = 0; j < tuples.length; j++) {
				for (int k = 0; k < 3; k++) {
					d = dval[k][j];

					if (d < 0 || d > 1)
						d = Double.NaN;

				}

				tuple.add(String.format("%.0f", dval[1][j] * 100.));
			}
		}

		aggr.sort(1, false);

		for (Tuple t : tuples) {
			File dir0 = new File(t.dir.getParent(), "exp0");

			UIo.saveFile(new File(dir0, "exp4_aggr.html"), new HtmlTable(
					"Aggregated", aggr).toSimpleString());

			UIo.saveFile(new File(dir0, "exp4_aggr.tex"),
					new TexTabular(aggr).toTexBodyString());
		}
	}

	private void saveTexFull() {
		TableTuple tuple;
		TableData full = new TableData();

		tuple = new TableTuple();
		tuple.add("WS", "Function", "Rnd", "Deg", "FB");
		full.add(tuple);

		for (Function function : functionSet) {
			tuple = new TableTuple();
			full.add(tuple);

			tuple.add(function.getService(), function.getName());

			for (Tuple j : tuples) {
				Exp4Tuple t = j.map.get(function);

				tuple.add(t == null ? "/" : t.toRatioString());
			}
		}

		full.sort();

		for (Tuple t : tuples) {
			File dir0 = new File(t.dir.getParent(), "exp0");

			UIo.saveFile(new File(dir0, "exp4_full.html"), new HtmlTable(
					"Full", full).toSimpleString());

			UIo.saveFile(new File(dir0, "exp4_full.tex"),
					new TexTabular(full).toTexString());
		}

	}
}
